<script lang="ts" name="index" setup>
import { PropType } from 'vue'
import { FormOptions } from './types'
import cloneDeep from 'lodash/cloneDeep'
import {
  FormInstance,
  UploadFile,
  UploadFiles,
  UploadProgressEvent,
  UploadRawFile,
  UploadUserFile
} from 'element-plus'

/** 上传组件要发射的事件 */
const emits = defineEmits([
  'onPreview',
  'onRemove',
  'onSuccess',
  'onError',
  'onProgress',
  'onChange',
  'onExceed',
  'beforeUpload',
  'beforeRemove'
])

const props = defineProps({
  options: {
    type: Array as PropType<FormOptions[]>,
    default: () => [] as PropType<FormOptions[]>,
    require: true
  },
  httpRequest: {
    type: Function,
    default: function () {
      // console.log('自定义请求方法')
    }
  }
})

const model = ref<any>(null)
const rules = ref<any>(null)
const formRef = ref<FormInstance>()

/** 初始化form的 model 和 rules */
const initForm = () => {
  if (props?.options?.length) {
    const formItem: any = {}
    const formRules: any = {}

    props.options.map((item: FormOptions) => {
      formItem[item.prop!] = item.value
      formRules[item.prop!] = item.rules
    })

    model.value = cloneDeep(formItem)
    rules.value = cloneDeep(formRules)
  }
}

/** 重置表单 */
const resetFields = () => {
  formRef.value!.resetFields()
}

/** 验证表单 */
const validate = () => {
  return formRef.value!.validate()
}

/** 获取表单参数 */
const getFormParams = () => {
  return model.value
}

/** 分发事件 */
defineExpose({
  resetFields,
  validate,
  getFormParams
})

onMounted(() => {
  initForm()
})

/** 监听props属性 options 的变化 */
watch(
  () => props.options,
  () => {
    initForm()
  },
  { deep: true }
)

/** 上传组件的方法 */
const onPreview = (uploadFile: UploadFile) => {
  emits('onPreview', uploadFile)
}

const onRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits('onRemove', { uploadFile, uploadFiles })
}

const onSuccess = (
  response: any,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  const uploadItem = props.options.find((item) => item.type === 'upload')!
  model.value[uploadItem.prop!] = { response, uploadFile, uploadFiles }
  emits('onSuccess', { response, uploadFile, uploadFiles })
}

const onError = (
  error: Error,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  emits('onError', { error, uploadFile, uploadFiles })
}

const onProgress = (
  evt: UploadProgressEvent,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  emits('onProgress', { evt, uploadFile, uploadFiles })
}

const onChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits('onChange', { uploadFile, uploadFiles })
}

const onExceed = (files: File[], uploadFiles: UploadUserFile[]) => {
  emits('onExceed', { files, uploadFiles })
}

const beforeUpload = (rawFile: UploadRawFile) => {
  emits('beforeUpload', rawFile)
}

const beforeRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits('beforeRemove', { uploadFile, uploadFiles })
}
</script>

<template>
  <el-form
    v-if="model"
    ref="formRef"
    v-bind="$attrs"
    :model="model"
    :rules="rules"
    :validate-on-rule-change="false"
  >
    <template v-for="(item, index) in props.options" :key="index">
      <el-form-item :label="item.label" :prop="item.prop">
        <template v-if="item.type !== 'upload' && item.type !== 'editor'">
          <component
            :is="`el-${item.type}`"
            v-bind="item.attrs"
            v-model="model[item.prop!]"
          >
            <template v-if="item?.children?.length">
              <template v-for="(child, key) in item.children" :key="key">
                <component
                  v-bind="child.attrs"
                  :is="`el-${child.type}`"
                  :label="child.label"
                  :value="child.value"
                />
              </template>
            </template>
          </component>
        </template>
        <template v-if="item.type === 'upload'">
          <el-upload
            v-bind="item.uploadAttrs"
            :on-preview="onPreview"
            :on-remove="onRemove"
            :on-success="onSuccess"
            :on-error="onError"
            :on-progress="onProgress"
            :on-change="onChange"
            :on-exceed="onExceed"
            :before-upload="beforeUpload"
            :before-remove="beforeRemove"
            :http-request="httpRequest"
          >
            <slot name="uploadArea"></slot>
            <template #tip>
              <slot name="uploadTip"></slot>
            </template>
          </el-upload>
        </template>
        <template v-if="item.type === 'editor'"> </template>
      </el-form-item>
    </template>
    <el-form-item>
      <slot name="action" :form="formRef" :model="model"></slot>
    </el-form-item>
  </el-form>
</template>
